Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization

''' <summary>
''' Web radio tuning engine. Core engine of QuickTuner.
''' </summary>
''' <remarks></remarks>
Public Class Radio

    Dim WithEvents _hiddenBrowser As WebBrowser

    Dim _sl As StationList
    Dim _isPlaying As Boolean

#Region "Events"

    ''' <summary>
    ''' Raised when current station has been changed.
    ''' </summary>
    ''' <param name="sender">The object which raises the event.</param>
    ''' <param name="e">Event arguments.</param>
    ''' <remarks></remarks>
    Public Event StationChanged(ByVal sender As Object, ByVal e As EventArgs)

    ''' <summary>
    ''' Raised when the radio changes state. e.g. from play to stop or vice versa.
    ''' </summary>
    ''' <param name="sender">The object which raises this event.</param>
    ''' <param name="e">Event arguments.</param>
    ''' <remarks></remarks>
    Public Event PlayStateChanged(ByVal sender As Object, ByVal e As EventArgs)

#End Region

#Region "Ctor and inits"

    ''' <summary>
    ''' Creates a new radio object with an empty station list.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
        init(Nothing)
    End Sub

    ''' <summary>
    ''' Creates a new radio object and load list of stations from specified filename.
    ''' </summary>
    ''' <param name="filename">Station list filename to load from.</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal filename As String)
        init(filename)
    End Sub

    ''' <summary>
    ''' Initializes the radio.
    ''' </summary>
    ''' <param name="filename">Nothing or a filename to load list of stations.</param>
    ''' <remarks></remarks>
    Protected Sub init(ByVal filename As String)

        'stations list file supplied?
        If filename IsNot Nothing Then
            Me.LoadStationList(filename)
        Else
            _sl = New StationList()
        End If

        'state initializations
        _isPlaying = False

        'hidden browser to let the webpage executes in
        _hiddenBrowser = New WebBrowser()

    End Sub

#End Region

#Region "Properties"

    ''' <summary>
    ''' Gets or sets currently playing station.
    ''' </summary>
    ''' <value>New current station.</value>
    ''' <returns>Current station.</returns>
    ''' <remarks></remarks>
    Public Property CurrentStation() As OnlineStation
        Get
            Return _sl.CurrentStation
        End Get
        Set(ByVal value As OnlineStation)
            Me.GotoStation(value)
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets currently playing station index.
    ''' </summary>
    ''' <value>Index of new current station.</value>
    ''' <returns>Index of current station.</returns>
    ''' <remarks></remarks>
    Public Property CurrentStationIndex() As Integer
        Get
            Return _sl.CurrentStationIndex
        End Get
        Set(ByVal value As Integer)
            Me.GotoStation(_sl.CurrentStationIndex)
        End Set
    End Property

    ''' <summary>
    ''' Gets a list of stations.
    ''' </summary>
    ''' <value>List of stations.</value>
    ''' <returns>List of stations.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Stations() As List(Of OnlineStation)
        Get
            Return _sl.Stations
        End Get
    End Property

    ''' <summary>
    ''' Gets the station with the specified index.
    ''' </summary>
    ''' <param name="index">Index of the station you want.</param>
    ''' <value>Index of the station.</value>
    ''' <returns>Station with specified index as an OnlineStation instance.</returns>
    ''' <remarks></remarks>
    Default Public ReadOnly Property Item(ByVal index As Integer) As OnlineStation
        Get
            Return _sl(index)
        End Get
    End Property

    ''' <summary>
    ''' Gets wether the playing something.
    ''' </summary>
    ''' <value></value>
    ''' <returns>True if the player is playing a webpage, False otherwise.</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property IsPlaying() As Boolean
        Get
            Return _isPlaying
        End Get
    End Property

#End Region

#Region "Usual control code"

    ''' <summary>
    ''' Plays currently selected station.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub PlayCurrentStation()

        'browse to the specified webpage in the hidden browser
        _hiddenBrowser.Navigate(_sl.CurrentStation.URL)

        'notify of player state changes, if is not already playing
        If Not _isPlaying Then
            _isPlaying = True
            notifyStateChange()
        End If

    End Sub

    ''' <summary>
    ''' Stops currently playing station.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub StopCurrentStation()

        'just navigates to a blank page
        _hiddenBrowser.Navigate("about:blank")

        'notify of player state changes, if it has not stopped before
        If _isPlaying Then
            _isPlaying = False
            notifyStateChange()
        End If

    End Sub

    ''' <summary>
    ''' Toggles the radio state, stopping it if it is playing and plays it if it has been
    ''' stopped.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub PlayToggle()

        'calls the appropriate stop/play method
        If _isPlaying Then
            StopCurrentStation()
        Else
            PlayCurrentStation()
        End If

    End Sub

    ''' <summary>
    ''' Sets currently playing station to a station with the specified index.
    ''' </summary>  
    ''' <param name="index">Index of the station to change to.</param>
    ''' <remarks>If radio is playing something, it will stop and play
    ''' this newly selected station instead.</remarks>
    Public Sub GotoStation(ByVal index As Integer)

        'no station change neceessary?
        If _sl.CurrentStationIndex = index Then Return

        'change to new station
        _sl.CurrentStationIndex = index
        notifyStationChange()

        'if already playing, stops and play the new station instead
        If _isPlaying Then PlayCurrentStation()

    End Sub

    ''' <summary>
    ''' Sets currently playing station to specified station.
    ''' </summary>
    ''' <param name="os">The station to set as new station.</param>
    ''' <remarks>If radio is playing something, it will stop and play
    ''' the newly selected station instead.</remarks>
    Public Sub GotoStation(ByVal os As OnlineStation)
        GotoStation(_sl.Stations.IndexOf(os))
    End Sub

    ''' <summary>
    ''' Sets currently playing station to the next station on the list.
    ''' </summary>
    ''' <remarks>If radio is playing something, it will stop and play
    ''' the newly selected station instead.</remarks>
    Public Sub NextStation()

        'next station, please
        _sl.NextStation()
        notifyStationChange()

        'if already playing, play this new station instead.
        If _isPlaying Then PlayCurrentStation()

    End Sub

    ''' <summary>
    ''' Sets currently playing station to the previous station on the list.
    ''' </summary>
    ''' <remarks>If radio is playing something, it will stop and play
    ''' the newly selected station instead.</remarks>
    Public Sub PreviousStation()

        'previous station, please
        _sl.PrevStation()
        notifyStationChange()

        'if already playing, play this new station instead.
        If _isPlaying Then PlayCurrentStation()

    End Sub

#End Region

#Region "Station list manipulation"

    ''' <summary>
    ''' Loads list of stations from a file.
    ''' </summary>
    ''' <param name="filename">The station list filename to load from.</param>
    ''' <param name="append">Specify true to append stations to current list, false to replace current list instead.</param>
    ''' <remarks>This will stop all radio playing, if any.</remarks>
    Public Sub LoadStationList(ByVal filename As String, Optional ByVal append As Boolean = False)

        'stream content from a file
        Using fs = New FileStream(filename, FileMode.Open)
            LoadStationList(fs, append)
            fs.Close()
        End Using

    End Sub

    ''' <summary>
    ''' Loads list of stations from a stream.
    ''' </summary>
    ''' <param name="stream">The stream to load station list data from.</param>
    ''' <param name="append">Specify true to append stations to current list, false to replace current list instead.</param>
    ''' <remarks>This will stop all radio playing, if any.</remarks>
    Public Sub LoadStationList(ByVal stream As Stream, Optional ByVal append As Boolean = False)

        'stops any playing stations first
        If _isPlaying Then StopCurrentStation()

        'deserialize station list from the stream
        Dim sr As New StreamReader(stream)
        Dim xs As New XmlSerializer(GetType(StationList))
        Dim newList As StationList

        newList = CType(xs.Deserialize(sr), StationList)

        'if in replace mode
        If Not append Then
            _sl = newList 'replace with new list

        Else 'else append to current list
            _sl.Stations.AddRange(newList.Stations)

        End If

        'NOTE:don't close or dispose the streamwriter immediatly
        'or else the underlying stream will also close automatically
        'which is not the intended side effects
        'sr.Dispose()

        'notify of station change
        notifyStationChange()

    End Sub

    ''' <summary>
    ''' Saves current list of stations to a file.
    ''' </summary>
    ''' <param name="filename">The station list filename to save to.</param>
    ''' <remarks>Does not interfere with radio playing.</remarks>
    Public Sub SaveStationList(ByVal filename As String)

        'opens a files stream for writing
        Using fs = New FileStream(filename, FileMode.Open)
            SaveStationList(fs)
            fs.Close()
        End Using

    End Sub

    ''' <summary>
    ''' Saves current list of stations to a stream.
    ''' </summary>
    ''' <param name="stream">The stream to save station list to.</param>
    ''' <remarks>Does not interfere with radio playing.</remarks>
    Public Sub SaveStationList(ByVal stream As Stream)

        'serialize the staiton to the stream
        Dim sw As New StreamWriter(stream)
        Dim xs As New XmlSerializer(GetType(StationList))

        xs.Serialize(sw, _sl)

        'NOTE:just flush, don't close or dispose immediatly
        'or else the underlyings stream will also close
        'which wasn't the intended consequences of this method
        sw.Flush()
        'sw.Dispose()

    End Sub

#End Region

    ''' <summary>
    ''' Notify event subscribers of changed current station.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Sub notifyStationChange()
        RaiseEvent StationChanged(Me, New EventArgs())
    End Sub

    ''' <summary>
    ''' Notify event subscribers of changed radio states.
    ''' </summary>
    ''' <remarks></remarks>
    Protected Sub notifyStateChange()
        RaiseEvent PlayStateChanged(Me, New EventArgs())
    End Sub

End Class
